package com.gframework.antibrush.exec;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;

import com.gframework.antibrush.limiter.CurrentLimiter;
import com.gframework.antibrush.limiter.LimitRequest;

/**
 * {@link LimitRequest}注解的限流实现类.
 * <p>通过给方法增加{@link LimitRequest}注解即可进行限流。限流的数据存储方式有多种，一种是内存存储，一种是reids存储。
 * 无需手动进行切换，组件会根据当前环境是否具有可用的redis而自己进行选择。redis的优先级更高。
 * 
 * @since 1.0.0
 * @author Ghwolf
 */
@Aspect
@Order(-10000)
@Import({ DefaultLimiterNoPassHandler.class })
public class LimitRequestAOP {
	
	@Autowired
	private HttpServletRequest request ;
	
	@Autowired
	private HttpServletResponse response;
	/**
	 * 限流器
	 */
	@Autowired
	private CurrentLimiter currentLimiter ;
	/**
	 * 限流不通过返回结果处理器
	 */
	@Autowired
	private LimiterNoPassHandler limiterNoPassHandler ;
	
	@Around("@annotation(com.gframework.antibrush.limiter.LimitRequest) || @within(com.gframework.antibrush.limiter.LimitRequest)")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
		MethodSignature methodSign = (MethodSignature) joinPoint.getSignature();
		Method method = methodSign.getMethod();
		LimitRequest anno = method.getAnnotation(LimitRequest.class);
		if (anno == null) {
			anno = method.getDeclaringClass().getAnnotation(LimitRequest.class);
		}
		String key = anno.limitingMode().newInstance().getKey(anno, method, request);
		
		// 速率算法
		if (this.currentLimiter.rate(anno,key)) {
			return joinPoint.proceed(joinPoint.getArgs());
		}
		
		// 禁止通行
		return this.limiterNoPassHandler.returnValue(request, response);
	}
	
}
